package com.smgeek.gkrpc.client;

import com.smgeek.gkrpc.Request;
import com.smgeek.gkrpc.Response;
import com.smgeek.gkrpc.ServiceDescriptor;
import com.smgeek.gkrpc.codec.Decoder;
import com.smgeek.gkrpc.codec.Encoder;
import com.smgeek.gkrpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import sun.misc.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @ClassName RemoteInvoker
 * @Author wuyuyang
 * @Date 2022/1/7 14:31
 * @Description RemoteInvoker
 * @Version 1.0
 */
@Slf4j
public class RemoteInvoker implements InvocationHandler {
    private Class clazz;
    private Encoder encoder;
    private Decoder decoder;
    private TransportSelector selector;

    public RemoteInvoker(
            Class clazz,
            Encoder encoder,
            Decoder decoder,
            TransportSelector selector){
        this.clazz = clazz;
        this.encoder = encoder;
        this.decoder = decoder;
        this.selector = selector;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Request request = new Request();
        request.setServiceDescriptor(ServiceDescriptor.from(clazz, method));
        request.setParameters(args);

        Response resp = invokeRemote(request);
        if (resp == null || resp.getCode() != 0){
            throw new IllegalStateException("fail to invoke remote:" + resp);
    }
        return resp.getData();
    }

    private Response invokeRemote(Request request) {
        Response response = null;
        TransportClient client = null;



        try {
            client = selector.select();
            byte[] encode = encoder.encode(request);
            InputStream write = client.write(new ByteArrayInputStream(encode));
            byte[] bytes = IOUtils.readFully(write,write.available(),true);
            response = decoder.decode(bytes,Response.class);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            response.setCode(-1);
            response.setMessage("RpcClient got error:" + e.getClass() + ":" + e.getMessage());

        }finally {
            if(client!=null) {
                selector.release(client);
            }
        }
        return response;

    }




}
